﻿using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Common.Logging;

namespace Common.Logging.Loupe.Test
{
    [TestClass]
    public class LoupeLoggerTest
    {
        [TestMethod]
        public void TestInfo()
        {
            var logger = GetNewLogger();
            TestLevel(LogLevel.Info, logger, logger.Info);   
        }

        [TestMethod]
        public void TestDebug()
        {
            var logger = GetNewLogger();
            TestLevel(LogLevel.Debug, logger, logger.Debug);
        }

        [TestMethod]
        public void TestWarn()
        {
            var logger = GetNewLogger();
            TestLevel(LogLevel.Warn, logger, logger.Warn);
        }

        [TestMethod]
        public void TestTrace()
        {
            var logger = GetNewLogger();
            TestLevel(LogLevel.Trace, logger, logger.Trace);
        }

        [TestMethod]
        public void TestFatal()
        {
            var logger = GetNewLogger();
            TestLevel(LogLevel.Fatal, logger, logger.Fatal);
        }

        [TestMethod]
        public void TestOff()
        {
            LoupeLogger logger = GetNewLogger();
            logger.CurrentLevel = LogLevel.Off;

            bool didRun = false;
            logger.Info(s => didRun = true);

            Assert.IsFalse(didRun, "Value was set when level was off"); 
        
        }

        [TestMethod]
        public void TestAll()
        {
            LoupeLogger logger = GetNewLogger();
            logger.CurrentLevel = LogLevel.All;

            int count = 0;
            
            logger.Trace(s => count++);
            logger.Debug(s => count++);
            logger.Warn(s => count++);
            logger.Info(s => count++);
            logger.Error(s => count++);
            logger.Fatal(s => count++);
           
            Assert.AreEqual(6, count, "Not all log levels were recorded on LogLevel.All");
        }

        private void TestLevel(LogLevel level, LoupeLogger logger, Action<Action<FormatMessageHandler>> handler)
        {
            TestLevelUnder(level, logger, handler);
            TestLevelOver(level, logger, handler);
        }

        private LogLevel ShiftLevelUp(LogLevel level)
        {
            LogLevel nextLevel = LogLevel.Off;

            switch (level)
            {
                case LogLevel.All:
                    nextLevel = LogLevel.Trace;
                    break;

                case LogLevel.Trace:
                    nextLevel = LogLevel.Debug;
                    break;

                case LogLevel.Debug:
                    nextLevel = LogLevel.Info;
                    break;

                case LogLevel.Info:
                    nextLevel = LogLevel.Warn;
                    break;

                case LogLevel.Warn:
                    nextLevel = LogLevel.Error;
                    break;

                case LogLevel.Error:
                    nextLevel = LogLevel.Fatal;
                    break;

                case LogLevel.Fatal:
                    nextLevel = LogLevel.Off;
                    break;

                case LogLevel.Off:
                    nextLevel = LogLevel.Off;
                    break;

                default:
                    nextLevel = LogLevel.Off;
                    break;
            }

            return nextLevel;
       } 

        private LogLevel ShiftLevelDown(LogLevel level)
        {

            LogLevel nextLevel = LogLevel.Off;

            switch (level)
            {
                case LogLevel.All:
                    nextLevel = LogLevel.All;
                    break;

                case LogLevel.Trace:
                    nextLevel = LogLevel.All;
                    break;

                case LogLevel.Debug:
                    nextLevel = LogLevel.Trace;
                    break;

                case LogLevel.Info:
                    nextLevel = LogLevel.Debug;
                    break;

                case LogLevel.Warn:
                    nextLevel = LogLevel.Info;
                    break;

                case LogLevel.Error:
                    nextLevel = LogLevel.Warn;
                    break;

                case LogLevel.Fatal:
                    nextLevel = LogLevel.Error;
                    break;

                case LogLevel.Off:
                    nextLevel = LogLevel.Fatal;
                    break;

                default:
                    nextLevel = LogLevel.Off;
                    break;

            }

            return nextLevel;
        }
        
        private void TestLevelOver(LogLevel level, LoupeLogger logger, Action<Action<FormatMessageHandler>> handler)
        {   
            logger.CurrentLevel = ShiftLevelUp(level);

            bool didRun = false;
            handler(s => didRun = true);

            Assert.IsFalse(didRun, level.ToString() + " was called when level was set correctly.");
        }

        // Log level callbacks can be passed directly into this method
        private void TestLevelUnder(LogLevel level, LoupeLogger logger, Action<Action<FormatMessageHandler>> handler)
        {
            logger.CurrentLevel = level;

            bool didRun = false;
            handler(s => didRun = true);

            Assert.IsTrue(didRun,  level.ToString() + " was not called when level was set correctly."); 
        }

        private LoupeLogger GetNewLogger()
        {
            return new LoupeFactoryAdapter().GetLogger(typeof(LoupeLoggerTest)) as LoupeLogger;
        }
    }
}
